/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.cli;

import cz.mzk.kramerius.commons.Dom4jUtils;
import cz.mzk.oai.metadataRepository.metadata.MarcRecord;
import cz.mzk.oai.metadataRepository.metadata.MetadataFormat;
import cz.mzk.oai.metadataRepository.repository.AlephBase;
import cz.mzk.oai.metadataRepository.repository.Repository;
import cz.mzk.oai.metadataRepository.repository.collections.MarcBasedCollectionSelector;
import cz.mzk.oai.metadataRepository.repository.collections.MetadataCollection;
import cz.mzk.oai.metadataRepository.repository.records.Record;
import cz.mzk.oai.metadataRepository.repository.records.presentAndDeleted.PresentRecord;
import java.io.IOException;
import java.util.List;
import org.dom4j.Document;

/**
 * Finds items that are to be moved from this collection to other collections.
 * Change of assigning of item to collection can be caused by change in item's
 * marc record or by change of rules in MarcBasedCollectionSelector
 *
 * @author Martin Řehánek (rehan at mzk.cz)
 */
public class PrintPossibleMovesAction extends Action {

    //this value determined by profiling so that not too many 
    private static final int RECORDS_PROCESSED_BEFORE_GC = 500;
    private static final int RECORDS_PROCESSED_BEFORE_INFO_PRINT = 100;
    private final String originalCollectionName;
    private final MarcBasedCollectionSelector selector;
    private final AlephBase alephBase;

    public PrintPossibleMovesAction(Repository repository, String collectionName, String alephBase) {
        super(repository);
        this.originalCollectionName = collectionName;
        this.selector = new MarcBasedCollectionSelector(repository);
        this.alephBase = AlephBase.valueOf(alephBase);
    }

    @Override
    public void run() throws Exception {
        MetadataCollection collection = repository.findCollection(originalCollectionName);
        if (collection == null) {
            throw new Exception("No such collection '" + originalCollectionName + "'");
        }
        List<Record> records = collection.getRecords(MetadataFormat.marc21, null, null);
        CollectionsItemsMapping mapping = new CollectionsItemsMapping();
        int counter = 0;
        int movesFound = 0;
        System.gc();
        for (Record record : records) {
            counter++;
            String newCollectionName = findNewCollectionName(record);
            if (!originalCollectionName.equals(newCollectionName)) {
                mapping.addItem(record.getId(), newCollectionName);
                movesFound++;
            }
            if (counter % RECORDS_PROCESSED_BEFORE_GC == 0) {
                runGarbageCollector(true);
            }
            if (counter % RECORDS_PROCESSED_BEFORE_INFO_PRINT == 0) {
                System.out.println("processed " + counter + " items, found " + movesFound + " moves");

            }
        }
        printItemsInCollections(mapping);
    }

    private void runGarbageCollector(boolean withPrints) {
        Runtime runtime = Runtime.getRuntime();
        if (withPrints) {
            System.out.println("total memory: " + runtime.totalMemory());
            System.out.println("free  memory: " + runtime.freeMemory());
            System.out.println("running garbage collector");
        }
        System.gc();
        if (withPrints) {
            System.out.println("total memory: " + runtime.totalMemory());
            System.out.println("free  memory: " + runtime.freeMemory());
        }
    }

    private String findNewCollectionName(Record record) throws IOException {
        if (!record.isDeleted()) {
            PresentRecord present = (PresentRecord) record;
            MarcRecord marcRecord = new MarcRecord(present.getMetadata());
            try {
                MetadataCollection collection = selector.chooseCollectionOrThrowException(marcRecord, alephBase);
                return collection.getName();
            } catch (Throwable ex) {
                ex.printStackTrace();
                //means no collection found
                return "SYSTEM:unknown";
            }
        }
        //means marc record is deleted so collection cannot be determined
        return "SYSTEM:deleted";
    }

    private void printItemsInCollections(CollectionsItemsMapping mapping) {
        System.out.println("results:");
        Document printDoc = mapping.asXml();
        Dom4jUtils.writeDocument(printDoc, System.out, Dom4jUtils.PrintType.PRETTY);
    }
}
